/**
 * @file ZSTSequenceBuffer.h
 * @brief sequence buffer
 * @author babear.wangz
 * @date 2011-04-06
 */

#ifndef ZST_SequenceBuffer_H
#define ZST_SequenceBuffer_H

#include "ZTraits.h"
#include "ZSTEdgeAndNode.h"
#include "ZUtility.h"
#include "Swap.h"
#include <stdio.h>
#include <string.h>
#include <malloc.h>

/**
 * @class SequenceCopier
 * @brief copy sequence memory
 * @author babear.wangz
 * @date 2011-04-12
 */
template<typename T, bool needconstruct = ZTypeTraits<T>::need_construct>
class SequenceCopier;
template<typename T> class SequenceCopier<T, true> {
public:
    //copy [s, e) to the destination - dest
    static void Copy(const T* s, const T* e, T* dest)
    {
        while (s != e) {
            new (dest) T(*s);
            ++dest;
            ++s;
        }
    }
    // fill the [ds, de) with empty T
    static void FillWithEmpty(T* ds, T* de)
    {
        while (ds != de) {
            // fix me:
            new (ds) T();
            //new (ds) T(ZTypeTraits<T>::empty);
            ++ ds;
        }
    }
};
template<typename T> class SequenceCopier<T, false> {
public:
    //copy [s, e) to the destination - dest
    static void Copy(const T* s, const T* e, T* dest)
    {
        memcpy(dest, s, reinterpret_cast<const char*>(e) -
            reinterpret_cast<const char*>(s));
    }
    // fill the [ds, de) with empty T
    static void FillWithEmpty(T* ds, T* de)
    {
        while (ds != de) {
            (*ds) = ZTypeTraits<T>::empty;
            ++ds;
        }
    }
};

/**
 * @class ZSTSequenceBuffer
 * @biref buffer class for the suffix input string
 * @author babear.wangz
 * @date 2011-04-12
 */
template <typename T, typename Trait = ZTypeTraits<T>,
    typename BufferTrait = ZSTBufferSpaceParameter>
class ZSTSequenceBuffer {
public:// constructors and destructors
    /**
     * @brief constructor of ZSTSequenceBuffer class
     * @param cap the initial capacity for the buffer
     * @author babear.wangz
     * @date 2011-04-12
     */
    ZSTSequenceBuffer(uint32_t cap = BufferTrait::ZSTSequenceBufferInitSize);
    /**
     * @param buf the initial data which will be filled in this buffer
     * @param l the lengthof buf
     */
    ZSTSequenceBuffer(const T* buf, uint32_t l);
    // destructor
    ~ZSTSequenceBuffer();
public:
    // the necessary public methods whici must be implemented by a sequence
    // class
    /**
     * @fn PushBack
     * @brief push back an elmenent
     * @param elem the element
     * @author babear.wangz
     */
    bool PushBack(const T& elem);
    /**
     * @brief operator []
     * @param i the index
     */
    T& operator [] (size_t i);
    const T& operator [] (size_t i) const;
    //inline T* Buffer() { return buffer; }
    /**
     * @fn Equal
     * @brief compare []
     * @param s1 the first starting position
     * @param s2 the second starting position
     * @param l the length to be compared
     */
    bool Equal(uint32_t s1, uint32_t s2, uint32_t l) const;

    /**
     * @fn Swap
     * @brief swap with another ZSTSequenceBuffer object
     * @author babear.wangz
     * @date 2011-04-12
     */
    void Swap(ZSTSequenceBuffer& seqbuf);
    // get length of the buffer
    inline uint32_t Size() { return len; }
private:
    DISALLOW_COPY_AND_ASSIGN(ZSTSequenceBuffer);
    // buffer
    T* buffer;
    // length
    uint32_t len;
    // capacity
    uint32_t capacity;
};

// ZSTSequenceBuffer constructor
template <typename T, typename Trait, typename BufferTrait>
ZSTSequenceBuffer<T, Trait, BufferTrait>::ZSTSequenceBuffer(uint32_t cap)
: buffer(0)
, len(0)
, capacity(cap)
{
    if (capacity < BufferTrait::ZSTSequenceBufferInitSize) {
        capacity = BufferTrait::ZSTSequenceBufferInitSize;
    }
    buffer = (T*)malloc(sizeof(T)*capacity);
    ASSERT(buffer);
    SequenceCopier<T, Trait::need_construct>::FillWithEmpty(
        buffer, buffer+capacity);
/*
    if (Trait::need_construct) {
        for (uint32_t i = 0; i < capacity; ++i) {
            new (&buffer[i]) (T());
        }
    }*/
}

// ZSTSequenceBuffer constructor
template<typename T, typename Trait, typename BufferTrait>
ZSTSequenceBuffer<T, Trait, BufferTrait>::ZSTSequenceBuffer(
    const T* buf, uint32_t l)
: buffer(0)
, len(l)
, capacity(l)
{
    capacity = l;
    if (l < BufferTrait::ZSTSequenceBufferInitSize) {
        capacity = BufferTrait::ZSTSequenceBufferInitSize;
    }
    buffer = (T*)malloc(sizeof(T)*capacity);
    ASSERT(buffer);
    SequenceCopier<T, Trait::need_construct>::Copy(
        buf, buf + l, buffer);
    SequenceCopier<T, Trait::need_construct>::FillWithEmpty(
        buffer + l, buffer + capacity);
    /*if (Trait::need_construct) {
        uint32_t i = 0;
        for (; i < l; ++i) {
            new (&buffer[i]) (T(buf[i]));
        }
        for (; i < capacity; ++i) {
            new (&buffer[i]) (T());
        }
    } else {
        memcpy(buffer, buf, sizeof(T)*l);
    }*/
}

// ZSTSequenceBuffer destructor
template<typename T, typename Trait, typename BufferTrait>
ZSTSequenceBuffer<T, Trait, BufferTrait>::~ZSTSequenceBuffer()
{
    if (Trait::need_construct) {
        for (uint32_t i = 0; i < capacity; ++i) {
            (buffer + i)->~T();
        }
    }
    free(buffer);
}

// swap function
template<typename T, typename Trait, typename BufferTrait>
void ZSTSequenceBuffer<T, Trait, BufferTrait>::Swap(ZSTSequenceBuffer& seqbuf)
{
    ::Swap(buffer, seqbuf.buffer);
    ::Swap(len, seqbuf.len);
    ::Swap(capacity, seqbuf.capacity);
}

// pushback an element
template<typename T, typename Trait, typename BufferTrait>
bool ZSTSequenceBuffer<T, Trait, BufferTrait>::PushBack(const T& elem)
{
    if (len >= capacity) {
        ASSERT(len == capacity);
        uint32_t newcap = uint32_t(double(capacity) * 
            BufferTrait::ZSTSequenceBufferExtRate);
        ASSERT(newcap > len);
        T* tmpbuf = (T*)realloc(buffer, sizeof(T)*newcap);
        if (!tmpbuf) return false;
        buffer = tmpbuf;
        capacity = newcap;
        SequenceCopier<T, ZTypeTraits<T>::need_construct>::FillWithEmpty(
            buffer + len, buffer + capacity);
        /*if (Trait::need_construct) {
            for (uint32_t i = len; i < capacity; ++i) {
                new (&buffer[i]) (T());
            }
        }*/
    }
    buffer[len ++] = elem;
    return true;
}

// operator []
template<typename T, typename Trait, typename BufferTrait>
T& ZSTSequenceBuffer<T, Trait, BufferTrait>::operator [] (size_t i)
{
    ASSERT(i < len);
    return buffer[i];
}

template<typename T, typename Trait, typename BufferTrait>
const T& ZSTSequenceBuffer<T, Trait, BufferTrait>::operator [] (size_t i) const
{
    ASSERT(i < len);
    return buffer[i];
}

// equal function
template<typename T, typename Trait, typename BufferTrait>
bool ZSTSequenceBuffer<T, Trait, BufferTrait>::Equal(
    uint32_t s1, uint32_t s2, uint32_t l) const
{
    ASSERT(s1 <= len && s1+l <= len);
    ASSERT(s2 <= len && s2+l <= len);
    for (uint32_t i = 0; i < l; ++i) {
        if (buffer[s1+i] != buffer[s2+i]) return false;
    }
    return true;
}

#endif

